/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2024-2024. All rights reserved.
 *
 */
import os from 'os';

import { cloneDeep } from '@baize/wdk';

import { CoreModuleModelImpl } from '../model/module/core-module-model-impl.js';
import { ProjectModel } from '../model/project/project-model.js';
import { ProjectBuildProfile } from '../options/build/project-build-profile.js';
import { mergeBuildOptPath, readOpt } from '../project/build-option/build-option-util.js';
import { getElementFromArr } from '../utils/array-util.js';
import { findTargetProduct } from './find-target-product.js';
import { projectOhosConfigManager } from './global/project-ohos-config-manager.js';

import ProductBuildOpt = ProjectBuildProfile.ProductBuildOpt;
import { getJson5Obj } from '../utils/json-util.js';
import { AppJson } from '../options/configure/app-json-options.js';

class BuildOptionPath {
  private productOptPath: Map<string, Map<string, Attributes>> = new Map();
  private buildOptionPath: Record<string, Map<string, Map<string, Attributes>>> = {};
  private signConfigPath: Map<string, Attributes> = new Map();
  private appConfigPath: Map<string, Attributes> = new Map();

  setAppConfigPath(appJsonPath: string, appJsonOpt: AppJson.AppOptObj, srcPath: string): void {
    if (this.appConfigPath.size === 0) {
      this.initAppConfigPath(appJsonPath);
    }
    const newAppCigPath = readOpt(appJsonOpt?.app, srcPath);
    this.appConfigPath = mergePath(this.appConfigPath, newAppCigPath);
  }

  initAppConfigPath(appJsonPath: string): void {
    const appConfig = getJson5Obj(appJsonPath) as AppJson.AppOptObj;
    this.appConfigPath = readOpt(appConfig?.app, appJsonPath);
  }

  getAppConfigPath(appJson5Path: string, appConfigName: string): string {
    if (this.appConfigPath.size === 0) {
      this.initAppConfigPath(appJson5Path);
    }
    return this.appConfigPath.get(appConfigName)?.srcPath ?? 'app.json5 file of the project';
  }
  setProductOptPath(newMap: Map<string, Attributes>, product: string): void {
    const map = cloneDeep(this.productOptPath.get(product));
    if (!map || map.size === 0) {
      this.productOptPath.set(product, newMap);
      return;
    }
    this.productOptPath.set(product, mergePath(map, newMap));
  }

  setBuildOptionPath(moduleName: string, target: string, newMap: Map<string, Attributes>): void {
    if (!this.buildOptionPath[moduleName]) {
      this.buildOptionPath[moduleName] = new Map<string, Map<string, Attributes>>();
    }
    const map = cloneDeep(this.buildOptionPath[moduleName].get(target));
    if (!map || map.size === 0) {
      this.buildOptionPath[moduleName].set(target, newMap);
      return;
    }
    this.buildOptionPath[moduleName].set(target, mergePath(map, newMap, moduleName));
  }

  /**
   * 通过 product.Name 来获取对应的product buildOpt
   * @param productName
   */
  getProductOptPath(productName: string): Map<string, Attributes> | undefined {
    return this.productOptPath.get(productName);
  }

  /**
   *  获取到签名配置的路径来源
   * @param project
   * @param attributeName
   */
  getSignConfigPath(project: ProjectModel, attributeName: string): string {
    const optPath = [
      project.getProject().getBuildProfilePath(),
      " If there are no issues in this file,  perhaps you have used 'overrides' or 'hook' in 'project hvigorfile.ts', please check it.",
    ].join(`${os.EOL}\t`);
    return this.signConfigPath.get(attributeName)?.srcPath ?? optPath;
  }

  initSignConfig(project: ProjectModel, path: string) {
    let resultPath: Map<string, Attributes> = new Map();
    const overridesPath = readOpt(projectOhosConfigManager.getOverrides()?.signingConfig, project.getProject().getBuildFilePath() + '.ts');
    const currentProduct: ProductBuildOpt = findTargetProduct(project!);
    const app = project.getProfileOpt().app;
    const productSignConfigName = currentProduct.signingConfig;
    if (!productSignConfigName) {
      this.signConfigPath = mergePath(this.signConfigPath, overridesPath);
      return;
    }
    const signingConfig = getElementFromArr(app?.signingConfigs, productSignConfigName.substring(productSignConfigName.lastIndexOf('.') + 1));
    if (!signingConfig) {
      this.signConfigPath = mergePath(this.signConfigPath, overridesPath);
      return;
    }
    const signingConfigPath = readOpt(signingConfig, path);
    resultPath = mergeBuildOptPath(signingConfigPath, overridesPath);
    this.signConfigPath = mergePath(this.signConfigPath, resultPath);
  }

  /**
   * 获取对应字段的配置路径
   * @param moduleModel module
   * @param targetName target Name
   * @param attributesName 属性名称
   */
  getTargetBuildOptPath(moduleModel: CoreModuleModelImpl, targetName: string, attributesName: string): string {
    return (
      this.buildOptionPath[moduleModel.getName()]?.get(targetName)?.get(attributesName)?.srcPath ??
      [moduleModel.getProfilePath(), " If there are no issues in this file, perhaps you have used 'overrides' in 'hvigorfile.ts', please check it."].join(
        `${os.EOL}\t`,
      )
    );
  }
}

/**
 * 合并map 与 newMap，且以map为基准
 * 判断两个map里面存储的属性的值是否改变，如果改变就将newMap中的值和路径填到map中
 * 如果是模块级别的合并，两个map的值相等也优先存入路径中包含moduleName的路径
 *
 * @param map 原有的path Map
 * @param newMap 新的path Map
 * @param moduleName
 */
export function mergePath(map: Map<string, Attributes>, newMap: Map<string, Attributes>, moduleName?: string): Map<string, Attributes> {
  for (const [key, attributes] of map) {
    if (newMap.has(key)) {
      map = mergeKeyToMap(map, newMap, key, moduleName);
    }
  }
  return addKeyToMap(map, newMap);
}

function mergeKeyToMap(map: Map<string, Attributes>, newMap: Map<string, Attributes>, key: string, moduleName?: string): Map<string, Attributes> {
  const attributes = map.get(key);
  const newAttributes = newMap.get(key);
  if (attributes && newAttributes && newAttributes.value === attributes.value && moduleName) {
    const srcPath1 = attributes.srcPath;
    const srcPath2 = newAttributes.srcPath;
    if (!srcPath1.includes(moduleName) && srcPath2.includes(moduleName)) {
      map.set(key, newAttributes);
    }
  } else if (attributes && newAttributes && newAttributes.value !== attributes.value) {
    map.set(key, newAttributes);
  }
  return map;
}

function addKeyToMap(map: Map<string, Attributes>, newMap: Map<string, Attributes>): Map<string, Attributes> {
  for (const [key, value] of newMap) {
    if (!map.has(key)) {
      map.set(key, value);
    }
  }
  return map;
}

export const buildOptionPath = new BuildOptionPath();

export interface Attributes {
  value: string;
  srcPath: string;
}

/**
 * 通过 new Error的方法获取堆栈来找出调用hook函数的文件
 * 目前仅适配 setBuildProfileOpt() 方法
 */
export function getHookFilePath(): string {
  const stack = new Error().stack;
  const common = 'In the hvigorfile.ts or hvigorconfig.ts file of the module or project or in the plug-in';
  if (stack) {
    const strings = stack.split('\n');
    if (strings.length > 5) {
      const regex = /\(([^)]+)\)/; // 匹配括号内的内容

      // 此处获取堆栈中第四个路径,路径指向调用setBuildProfileOpt的hvigorfile.ts
      const match = strings[4].match(regex);
      return match ? match[1] : common;
    }
  }
  return common;
}
